package com.xbongbong.paas.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasAttachmentEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.enums.AddressEnum;
import com.xbongbong.paas.enums.AliasEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.EsAliasEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.PatternEnum;
import com.xbongbong.paas.enums.PlatFormEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.enums.SubFieldTypeEnum;
import com.xbongbong.paas.enums.TaskTypeEnum;
import com.xbongbong.paas.field.CharLimitsPoJo;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.UnitItemPoJo;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.pojo.MultiUnitPricePoJo;
import com.xbongbong.paas.pojo.dto.CoUserListDTO;
import com.xbongbong.paas.pojo.dto.FormDataRepeatDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.vo.CheckRepeatVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CalendarUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.RegexUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.AttrLenthConstant;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.domain.entity.FormConfigEntity;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.PaasStageEnum;
import com.xbongbong.pro.enums.RichTextEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.BomErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.BuinessDefaultErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ContactErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ContractErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.InstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.WorkReportErrorCodeEnum;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainDTO;
import com.xbongbong.pro.pojo.dto.PaasConfigGetDTO;
import com.xbongbong.pro.pojo.vo.PaasConfigGetVO;
import com.xbongbong.pro.service.toolbox.subform.PaasConfigService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.constant.ProductConstant;
import com.xbongbong.saas.constant.StockConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.FormConfigEnum;
import com.xbongbong.saas.enums.RepeatTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.ShowTypeEnum;
import com.xbongbong.saas.enums.UserTeamEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.CommunicatePlanEnum;
import com.xbongbong.saas.enums.business.ContactEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerCommunicateEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.enums.business.WorkReportFieldEnum;
import com.xbongbong.saas.enums.business.WorkReportPlanEnum;
import com.xbongbong.saas.help.FundHelp;
import com.xbongbong.saas.help.RelativeProductHelp;
import com.xbongbong.saas.model.FormConfigModel;
import com.xbongbong.saas.model.PaasFormDataModel;
import com.xbongbong.saas.service.toolbox.help.ProductHelp;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.model.CompanyConfigModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrOrDefaultFromFormData;

/**
 * @author: wufeng
 * @date: 2018/12/21 16:00
 * @desrcption: 数据信息教育
 */
@Component
public class FormDataValidateDataHelp {

    private static final Logger LOG = LoggerFactory.getLogger(FormDataValidateDataHelp.class);

    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private FormConfigModel formConfigModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private PaasFormDataModel paasFormDataModel;
    @Resource
    private FundHelp fundHelp;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private ProductHelp productHelp;
    @Resource
    private FormDataHelp formDataHelp;
    @Resource
    private FormDataFormExplainHelper formDataFormExplainHelper;
    @Resource
    private RepeatHelp repeatHelp;
    @Resource
    private RelativeProductHelp relativeProductHelp;
    @Resource
    private PaasConfigService paasConfigService;

    /**
     * 校验数据提交格式
     * @param validateDataDTO 数据对象
     * @throws XbbException 抛出业务异常
     */
    public void validateData(ValidateDataDTO validateDataDTO) throws XbbException{
        Integer businessType = validateDataDTO.getBusinessType();
        Integer isImport = validateDataDTO.getIsImport();
        try {
            JSONObject data = validateDataDTO.getData(); // 已经是经过解析的数据
            String corpid = validateDataDTO.getCorpid();
            Map<String, FieldAttrEntity> explainMap = validateDataDTO.getExplainMap();
            // 特殊：客户和线索的名称和电话是否判重要去表单配置表查询（此处重新给 explainMap 中的名称电话中的 noRepat 赋值）
            if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode()) || Objects.equals(businessType, XbbRefTypeEnum.CLUE.getCode())) {
                getCustomerRepeatConfig(validateDataDTO, explainMap, businessType);
            }
            //确认是否为创建回/付款单、其他收入/支出单
            SheetTypeEnum sheetTypeEnum = SheetTypeEnum.getByBusinessType(businessType);
            boolean sheetFlag = SheetTypeEnum.validateSheet(sheetTypeEnum);
            if (sheetFlag) {
                //如果是回/付款单、其他收入/支出单，则根据数据data内的回/付款单/其他收入/支出单类型重置sheetTypeEnum（前端传过来的businessType可能没有详细区分哪种类型回/付款单/其他收入/支出单）
                sheetTypeEnum = fundHelp.getSheetTypeEnumByData(sheetTypeEnum, data);
                //validateData只校验数据格式，字段是否禁止修改不用处理，默认置为true即可
                fundHelp.handlePaymentSheet4ExplainMap(true, sheetTypeEnum, explainMap, validateDataDTO.getDistributorMark());
            }
            Integer subFormRowNum = PaasConstant.SUB_FORM_MAX_LARGE;
            Integer selectProductRowNum = PaasConstant.SELECT_PRODUCT_MAX_ROW_LARGE;
            PaasConfigGetDTO paasConfigGetDTO = new PaasConfigGetDTO();
            paasConfigGetDTO.setCorpid(corpid);
            PaasConfigGetVO rowMaxNumJson = paasConfigService.getSubFormConfig(paasConfigGetDTO);
            if (Objects.nonNull(rowMaxNumJson)) {
                subFormRowNum = rowMaxNumJson.getSubFormRowNum();
                selectProductRowNum = rowMaxNumJson.getSelectProductRowNum();
            }
            //阶段相关字段不能去除
            List<String> stageRelateFields = new ArrayList<>();
            stageRelateFields.add(PaasStageEnum.STAGE_PROCESS_ID.getAttr());
            stageRelateFields.add(PaasStageEnum.STAGE_ID.getAttr());
            stageRelateFields.add(PaasStageEnum.STAGE_RATIO.getAttr());

            explainMap.entrySet().removeIf(entry-> (Objects.equals(entry.getValue().getVisible(), 0) && !stageRelateFields.contains(entry.getKey())
                    ||Objects.equals(entry.getValue().getIsOpen(), 0)  && !stageRelateFields.contains(entry.getKey()))
            );
            String platform = validateDataDTO.getPlatform();
            Iterator iterator = data.keySet().iterator();
            String numEsAlias = FieldTypeEnum.NUM.getEsalias();
            String  locationEsAlias = FieldTypeEnum.LOCATION.getEsalias();
            String  addressEsAlias = FieldTypeEnum.ADDRESS.getEsalias();
            // 特殊的逻辑校验
            Map<String, Boolean> validateMap = getValidateFieldMap(validateDataDTO, data, explainMap);

            // 高级权限的判断
            HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
            handlerExplainDTO.setOwnerId(Collections.singletonList(validateDataDTO.getOwnerId()));
            if (Objects.isNull(validateDataDTO.getCreatorId())) {
                handlerExplainDTO.setCreatorId(validateDataDTO.getUserId());
            } else {
                handlerExplainDTO.setCreatorId(validateDataDTO.getCreatorId());
            }
            BeanUtil.copyProperties(validateDataDTO, handlerExplainDTO, false);
            Long dataId = validateDataDTO.getDataId();
            Integer saasMark = validateDataDTO.getSaasMark();

            if (Objects.nonNull(validateDataDTO.getDataId())) {
                // 编辑时，字段权限判断，负责人协同人和创建人用旧数据的
                PaasFormDataEntityExt oldPaasFormDataEntity;
                if (Objects.equals(saasMark, SaasMarkEnum.PAAS.getCode())) {
                    oldPaasFormDataEntity = paasFormDataModel.getByKey(dataId, corpid);
                } else {
                    oldPaasFormDataEntity = formDataHelp.getFormData(dataId, corpid, businessType);
                }
                Map<Long, List<String>> mainUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<Long, List<String>> coUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Boolean hasTeam = UserTeamEnum.hasTeam(businessType);
                if (hasTeam) {
                    Boolean publicFlag = Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_PUBLIC.getCode()) || Objects.equals(businessType, XbbRefTypeEnum.CLUE_PUBLIC.getCode());
                    formDataFormExplainHelper.getUserIdMap(Collections.singletonList(dataId), corpid, businessType, publicFlag, mainUserMap, coUserMap);
                    List<String> mainUserList = mainUserMap.get(dataId);
                    if (CollectionsUtil.isNotEmpty(mainUserList)) {
                        handlerExplainDTO.setOwnerId(mainUserList);
                    }
                } else {
                    CoUserListDTO coUserListDTO = new CoUserListDTO();
                    BeanUtil.copyProperties(validateDataDTO, coUserListDTO);
                    coUserListDTO.setFormDataIdIn(Collections.singletonList(dataId));
                    coUserMap = formDataFormExplainHelper.coUserMap(coUserListDTO);
                }
                List<String> coUserList = coUserMap.get(dataId);
                if (CollectionsUtil.isNotEmpty(coUserList)) {
                    handlerExplainDTO.setCoUserId(coUserList);
                }
                // 设置创建人
                if (Objects.nonNull(oldPaasFormDataEntity)) {
                    if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), businessType)
                            && Objects.nonNull(oldPaasFormDataEntity.getData().get(CustomerManagementEnum.CREATOR.getAttr()))) {
                        handlerExplainDTO.setCreatorId(oldPaasFormDataEntity.getData().getString(CustomerManagementEnum.CREATOR.getAttr()));
                    } else {
                        handlerExplainDTO.setCreatorId(oldPaasFormDataEntity.getCreatorId());
                    }
                }
            }

            // 校验流水号的必填
            if (explainMap.containsKey(FieldTypeEnum.SERIALNO.getAlias())) {
                FieldAttrEntity fieldAttrEntity = explainMap.get(FieldTypeEnum.SERIALNO.getAlias());
                if (Objects.equals(fieldAttrEntity.getVisible(), 1) && Objects.equals(fieldAttrEntity.getStrictController(), BasicConstant.ZERO)) {
                    Boolean visible = !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
                    if (visible && Objects.equals(fieldAttrEntity.getRequired(), 1) && StringUtil.isEmpty(validateDataDTO.getSerialNo())) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, fieldAttrEntity.getAttrName());
                    }
                }
            }
            while (iterator.hasNext()){
                String key = iterator.next().toString();
                // 栗子公司ID的字段不做处理
                if (Objects.equals(key, CustomerManagementEnum.CLUE_COMPANY_ID.getAttr()) && Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())){
                    continue;
                }
                String[] array = key.split("_");
                FieldAttrEntity fieldAttrEntity = explainMap.get(key);
                if (fieldAttrEntity == null || Objects.equals(fieldAttrEntity.getVisible(), 0) || Objects.equals(fieldAttrEntity.getIsOpen(), 2)) {
                    continue;
                }
                String value = data.getString(key);
                //date_20为空，就将其置为0，修复bug41114
                if (Objects.equals(PaymentEnum.GENERATION_DATE.getAttr(), key) && StringUtil.isEmpty(value) && Objects.equals(XbbRefTypeEnum.PAYMENT.getCode(),validateDataDTO.getBusinessType())) {
                    value = String.valueOf(BasicConstant.ZERO);
                }
                if (Objects.equals(FieldTypeEnum.SERIALNO.getType(), fieldAttrEntity.getFieldType())) {
                    value = validateDataDTO.getSerialNo();
                }
                Integer fieldType = fieldAttrEntity.getFieldType();


                if (Objects.equals(fieldAttrEntity.getFieldType(),FieldTypeEnum.LINK_BUSINESS_SINGLE.getType()) || Objects.equals(fieldAttrEntity.getFieldType(),FieldTypeEnum.LINK_BUSINESS_MULTI.getType()) || Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.PRODUCT_STOCK.getType())){
                    //关联业务选择不在这校验，反正也是saas特殊字段
                    continue;
                }
                if (validateMap.containsKey(fieldAttrEntity.getAttr()) && !validateMap.get(fieldAttrEntity.getAttr())) {
                    continue;
                }
                if ( Objects.equals(businessType,XbbRefTypeEnum.CONTRACT.getCode()) && Objects.equals(validateDataDTO.getIsImport(), 1)
                        && (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.ADD_PAYMENT.getAttr())||Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.ADD_PAYMENT_SHEET.getAttr())||Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.ADD_WRITE_OFF_PREPAYMENT.getAttr()))) {
                    continue;
                }
                if ( Objects.equals(businessType,XbbRefTypeEnum.PURCHASE.getCode()) && Objects.equals(validateDataDTO.getIsImport(), 1)
                        && (Objects.equals(fieldAttrEntity.getAttr(), PurchaseEnum.ADD_PAYPLAN.getAttr())||Objects.equals(fieldAttrEntity.getAttr(), PurchaseEnum.ADD_PAY_SHEET.getAttr())||Objects.equals(fieldAttrEntity.getAttr(), PurchaseEnum.ADD_WRITE_OFF_PREPAY.getAttr()))) {
                    continue;
                }
                Boolean serialNoFlag = true;
                if (Objects.equals(fieldAttrEntity.getAttr(),FieldTypeEnum.SERIALNO.getAlias()) && Objects.equals(fieldAttrEntity.getStrictController(), BasicConstant.ONE)){
                    serialNoFlag = false;
                }
                //可见时，非空验证
                Boolean show = showOpenVisiable(fieldAttrEntity);
                if (show) {
                    // 高级权限不可见时不校验数据
                    Boolean visible = !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
                    if (Objects.equals(businessType,XbbRefTypeEnum.PRODUCT.getCode()) && (Objects.equals(key,ProductEnum.PRICE.getAttr()) || Objects.equals(key,ProductEnum.COST.getAttr()) || Objects.equals(key,ProductEnum.BARCODE.getAttr()))){
                        //沙雕产品开了多规格单价·字段就空了，必填校验特殊处理，望高人改之
                        JSONObject jsonObject = data.getJSONObject(ProductEnum.SPECIFICATION.getAttr());
                        if (!(Objects.nonNull(jsonObject) && Objects.equals(jsonObject.getBoolean("advancedMode"),true))){
                            if(visible && Objects.equals(1, fieldAttrEntity.getRequired()) && StringUtil.isEmpty(value)){
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, fieldAttrEntity.getAttrName());
                            }
                        }
                    }else {
                        //资金三期客户、合同导入应收账期的时候，excel是空的，值是取默认值的，但是校验字段在取默认值之前，所以这边加特殊逻辑
                        if (Objects.equals(validateDataDTO.getIsImport(), BasicConstant.ONE) &&
                                (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode()) || Objects.equals(businessType, XbbRefTypeEnum.CONTRACT.getCode())) &&
                                (Objects.equals(key, CustomerManagementEnum.RECEIVABLE_PERIOD.getAttr()) || Objects.equals(key, ContractEnum.RECEIVABLE_PERIOD.getAttr()))
                        ) {

                        } else if(visible && !Objects.equals(key, CommunicatePlanEnum.TARGET.getAttr()) && Objects.equals(1, fieldAttrEntity.getRequired()) && StringUtil.isEmpty(value) && serialNoFlag){
                            if (Objects.equals(validateDataDTO.getSubBusinessType(), XbbRefTypeEnum.CLUE_PUBLIC.getCode()) && (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.OWNERID.getType()) || Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.COUSERID.getType()))) {
                                // 公海池负责人和协同人系统默认是不可编辑，所以新建不校验必填
                                continue;
                            }
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, fieldAttrEntity.getAttrName());
                        }
                    }

                }
                String saasAttr = fieldAttrEntity.getSaasAttr();
                String esAlias = array[0];
                if (Objects.equals(1, fieldAttrEntity.getRequired()) && Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.STAR.getType()) && showOpenVisiable(fieldAttrEntity)) {
                    if (StringUtil.isEmpty(value) || Objects.equals(value, "0")) {
                        // 因为星级字段前端会给默认的0，所以校验必填时要加上0
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, explainMap.get(key).getAttrName());
                    }
                }
                //数字的处理
                if(esAlias.equals(numEsAlias)){
                    if (value != null && !Objects.equals(saasAttr, ProductEnum.PRICE.getSaasAttr())) {
                       //单价字段需要fieldType为num，但是传入的是对象，所以这里不予校验,后面存的时候会解析掉
                        if (FieldTypeEnum.NUM.getType().equals(fieldType) && value.length() > AliasEnum.NUM.getMax()) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.MORE_THAN_LENGTH, explainMap.get(key).getAttrName(), AliasEnum.NUM.getMax());
                        }
                    }
                    if(!Objects.equals(saasAttr, ProductEnum.STOCK.getSaasAttr()) && !Objects.equals(saasAttr, ProductEnum.PRICE.getSaasAttr())){
                        // 产品库存,单价字段需要fieldType为num，但是传入的是对象，所以这里不予校验
                        String num = data.getString(key);
                        if(num == null) {
                            continue;
                        }
                        validateNum(key, explainMap, data, businessType,isImport);
                    }
                } else if (Objects.equals(FieldTypeEnum.DATETIME.getType(), fieldType) && Objects.equals(fieldAttrEntity.getIsRedundant(), 1)) {
                    // 日期校验先去掉吧，有很多日期要改的，后面稍微空闲点再加
                    validateDateTime(key, explainMap, data);
                } else if(esAlias.equals(locationEsAlias)) {
                    //定位
                    if(platform.equals(PlatFormEnum.WEB.getValue())) {
                        continue;
                    }
                    validateLocation(key, explainMap, data);
                } else if (esAlias.equals(addressEsAlias)){
                    //地址
                    validateAddress(key, explainMap, data);
                } else if (Objects.equals(fieldType,FieldTypeEnum.SELECT_PRODUCT.getType())){
                    JSONArray jsonArray = data.getJSONArray(key);
                    String parentAttrName = explainMap.get(key).getAttrName();
                    List<? extends FieldAttrEntity> subJSONArray = explainMap.get(key).getSubForm().getItems();
                    Map<String, FieldAttrEntity> subExplainMap = ExplainUtil.getExplainMapByList(subJSONArray);
                    if (Objects.equals(fieldAttrEntity.getRequired(), 1)) {
                        if (jsonArray.isEmpty()) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, explainMap.get(key).getAttrName());
                        }
                    }
                    if (Objects.isNull(jsonArray)) {
                        continue;
                    }
                    // 限制产品数量不超过260
                    boolean neeLimitProductNum = XbbRefTypeEnum.hasProductForm(businessType);
                    if (neeLimitProductNum && jsonArray.size() > selectProductRowNum) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205048, String.format(ProductErrorCodeEnum.API_ERROR_205048.getMsg(), selectProductRowNum));
                    }
                    // 校验子表单的必填
                    validateRequire(jsonArray, fieldAttrEntity, subExplainMap, handlerExplainDTO);

                    // 序列号单据保存前置查重校验
                    Boolean isJxcUse = paasAppModel.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
                    // 查看公司级配置
                    CompanyConfigEntity configEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.ENABLE_SEQ_MANAGEMENT.getAlias(), corpid);
                    // 是否审批通过
                    TaskTypeEnum taskTypeEnum = validateDataDTO.getTaskTypeEnum();
                    Boolean isTaskCommit = Objects.nonNull(taskTypeEnum) && Objects.equals(taskTypeEnum, TaskTypeEnum.COMMIT);
                    Long formDataId = validateDataDTO.getFormDataId();
                    if (isJxcUse && Objects.nonNull(configEntity) && Objects.equals("1", configEntity.getConfigValue()) && StockConstant.SERIAL_TYPE_LIST.contains(businessType) && isTaskCommit && formDataId == 0) {
                        productHelp.preCheckSaveSerial(data, jsonArray, key, businessType, corpid);
                    }

                    // 合同，机会，报价单应用价目表
                    if ((Objects.equals(businessType, XbbRefTypeEnum.CONTRACT.getCode()) || Objects.equals(businessType, XbbRefTypeEnum.SALES_OPPORTUNITY.getCode()) || Objects.equals(businessType, XbbRefTypeEnum.QUOTATION.getCode())) && (!Objects.equals(validateDataDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode()))) {
                        // 是否启用价目表
                        CompanyConfigEntity priceConfig = companyConfigModel.getByConfigAlias(CompanyConfigEnum.PRICE_MANAGE.getAlias(), corpid);
                        if (Objects.nonNull(priceConfig) && Objects.equals(priceConfig.getConfigValue(), "1")) {
                            relativeProductHelp.preCheckSavePrice(corpid, validateDataDTO.getFormId(), data, key, businessType);
                        }
                    }
                    Long unit;
                    for (int i = 0;i < jsonArray.size();i++) {
                        ValidateDataDTO subValidateData = new ValidateDataDTO();
                        BeanUtil.copyProperties(validateDataDTO, subValidateData);
                        JSONObject subData = jsonArray.getJSONObject(i);
                        subValidateData.setData(subData);
                        subValidateData.setExplainMap(subExplainMap);
                        Integer enableMultiUnit = subData.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getSaasAttr());
                        UnitItemPoJo baseUnitItem = null;
                        UnitItemPoJo businessUnitItem = null;
                        boolean needSaveBusinessUnitMessage = false;
                        if (Objects.equals(enableMultiUnit, 1)) {
                            unit = subData.getLong(SelectProductEnum.UNIT.getAttr());
                            List<UnitItemPoJo> unitItemPoJos = getJsonArrOrDefaultFromFormData(subData, ProductConstant.TRANSFORM_UNIT_RATE, new JSONArray()).toJavaList(UnitItemPoJo.class);
                            for (UnitItemPoJo unitItemPoJo : unitItemPoJos) {
                                if (Objects.equals(unitItemPoJo.getIsBase(), 1)) {
                                    baseUnitItem = unitItemPoJo;
                                }
                                if (Objects.equals(unitItemPoJo.getValue(), unit)) {
                                    businessUnitItem = unitItemPoJo;
                                }
                                if (Objects.nonNull(baseUnitItem) && Objects.nonNull(businessUnitItem)) {
                                    break;
                                }
                            }
                            needSaveBusinessUnitMessage = Objects.nonNull(businessUnitItem);
                        }
                        for (Map.Entry<String,FieldAttrEntity> entityEntry : subExplainMap.entrySet()){
                            FieldAttrEntity subFieldAttrEntity = entityEntry.getValue();
                            String attr = entityEntry.getKey();
                            if (Objects.equals(subFieldAttrEntity.getIsRedundant(),0)) {
                                SelectProductEnum selectProductEnum = SelectProductEnum.getAttrByAttr(attr);
                                switch (selectProductEnum) {
                                    case NUM:
                                        Double productNum;
                                        try {
                                            productNum = subData.getDouble(attr);
                                        } catch (Exception e){
                                            LOG.error("formDataValidateDataHelp.validateData  校验数字", e);
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, I18nStringConstant.ENTER_NUMBER, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        }
                                        if (productNum == null) {
                                            throw new XbbException(ContractErrorCodeEnum.API_ERROR_203012, I18nStringConstant.NOT_EXIST, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        }
                                        if (needSaveBusinessUnitMessage) {
                                            subData.put(SelectProductEnum.NUM.getAttr(), Arith.mul(productNum, businessUnitItem.getRate()));
                                            subData.put(SelectProductEnum.BUSINESS_NUM.getAttr(), productNum);
                                            subData.put(SelectProductEnum.BUSINESS_UNIT.getAttr(), businessUnitItem.getValue());
                                            subData.put(SelectProductEnum.RATE.getAttr(), businessUnitItem.getRate());
                                            subData.put(SelectProductEnum.UNIT_GROUP.getAttr(), businessUnitItem.getGroupId());
                                            subData.put(SelectProductEnum.UNIT.getAttr(), baseUnitItem.getValue());
                                            productNum = Arith.mul(productNum, businessUnitItem.getRate());
                                        }
                                        if (!Objects.equals(businessType, XbbRefTypeEnum.INVENTORY.getCode()) && productNum <= 0) {
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, I18nStringConstant.GREATER_ZERO, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        } else if (Objects.equals(businessType, XbbRefTypeEnum.INVENTORY.getCode())) {
                                            if (productNum < 0){
                                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, I18nStringConstant.GREATER_OR_EQUAL_ZERO, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                            }
                                        }else if (productNum <= 0){
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, I18nStringConstant.GREATER_ZERO, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        }
                                        break;
                                    case SELLING_PRICE:
                                        Double productPrice;
                                        try {
                                            productPrice = subData.getDouble(attr);
                                        } catch (Exception e){
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, I18nStringConstant.ENTER_NUMBER, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        }
                                        if (productPrice == null) {
                                            throw new XbbException(ContractErrorCodeEnum.API_ERROR_203012, I18nStringConstant.NOT_EXIST, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        }
                                        if (productPrice < 0){
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, I18nStringConstant.GREATER_OR_EQUAL_ZERO, parentAttrName + "." + subFieldAttrEntity.getAttrName());
                                        }
                                        if (needSaveBusinessUnitMessage) {
                                            subData.put(SelectProductEnum.SELLING_PRICE.getAttr(), Arith.div(productPrice, businessUnitItem.getRate(), subFieldAttrEntity.getAccuracy()));
                                            subData.put(SelectProductEnum.BUSINESS_SELLING_PRICE.getAttr(), productPrice);
                                        }
                                        break;
                                    case COST:
                                        Double cost = subData.getDouble(attr);
                                        if (Objects.equals(businessType, XbbRefTypeEnum.REFUND.getCode()) && Objects.isNull(cost)) {
                                            cost = 0D;
                                        }
                                        if (Objects.equals(businessType, XbbRefTypeEnum.COST_ADJUST.getCode()) && Objects.isNull(cost)) {
                                            cost = 0D;
                                        }
                                        if (cost == null) {
                                            String warehouseName= "";
                                            if (Objects.nonNull(subData.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr())) && !subData.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr()).isEmpty()) {
                                                warehouseName = subData.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr()).getJSONObject(0).getString(StringConstant.SAAS_LINK_BUSINESS_NAME);
                                            }
                                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205050,String.format(ProductErrorCodeEnum.API_ERROR_205050.getMsg(), i + 1, parentAttrName, Objects.toString(warehouseName, ""), subFieldAttrEntity.getAttrName()));
                                        }else if (cost < 0 && !Objects.equals(businessType, XbbRefTypeEnum.COST_ADJUST.getCode())) {
                                            //出库单新增判断是否开了负库存出库配置
                                            if (ProductConstant.OUTSTOCK_TYPE_LIST.contains(businessType)){
                                                FormConfigEntity outStockRuleConfigEntity = formConfigModel.getEntityByBusinessType(corpid, XbbRefTypeEnum.OUTSTOCK.getCode(), FormConfigEnum.OUTSTOCK_INSUFFICIENT_NOT_ALLOW_WAREHOUSING.getConfig());
                                                //部分公司没有配置表示不开启负库存
                                                boolean isCheck = Objects.nonNull(outStockRuleConfigEntity) && Objects.equals(outStockRuleConfigEntity.getConfigValue(),"0");
                                                if (!isCheck){
                                                    throw new XbbException(InstockErrorCodeEnum.API_ERROR_210023, I18nStringConstant.GREATER_OR_EQUAL_ZERO, parentAttrName + "." + subData.getString(SelectProductEnum.COST.getAttr()) + "." + subFieldAttrEntity.getAttrName());
                                                }
                                            }else {
                                                throw new XbbException(InstockErrorCodeEnum.API_ERROR_210023, I18nStringConstant.GREATER_OR_EQUAL_ZERO, parentAttrName + "." + subData.getString(SelectProductEnum.COST.getAttr()) + "." + subFieldAttrEntity.getAttrName());
                                            }
                                        }
                                        if (needSaveBusinessUnitMessage) {
                                            subData.put(SelectProductEnum.COST.getAttr(), Arith.div(cost, businessUnitItem.getRate(), subFieldAttrEntity.getAccuracy()));
                                            subData.put(SelectProductEnum.BUSINESS_COST.getAttr(), cost);
                                        }
                                        if (Objects.isNull(subData.get(SelectProductEnum.TOTAL_COST.getAttr())) && !Objects.equals(businessType, XbbRefTypeEnum.COST_ADJUST.getCode())) {
                                            subData.put(SelectProductEnum.TOTAL_COST.getAttr(), Arith.mul(cost, Objects.isNull(subData.get(SelectProductEnum.BUSINESS_NUM.getAttr())) ? subData.getDouble(SelectProductEnum.NUM.getAttr()) : subData.getDouble(SelectProductEnum.BUSINESS_NUM.getAttr())));
                                        }
                                        break;
                                    case PRICE:
                                        Double price = subData.getDouble(attr);
                                        JSONArray priceArr = subData.getJSONArray(ProductConstant.PRICE);
                                        if (needSaveBusinessUnitMessage){
                                            if (Objects.nonNull(priceArr)) {
                                                Map<Long, Double> priceMap = priceArr.toJavaList(MultiUnitPricePoJo.class).stream().collect(Collectors.toMap(MultiUnitPricePoJo::getValue, MultiUnitPricePoJo::getRate));
                                                subData.put(SelectProductEnum.PRICE.getAttr(), priceMap.getOrDefault(baseUnitItem.getValue(), 0D));
                                                subData.put(SelectProductEnum.BUSINESS_PRICE.getAttr(), price);
                                            } else {
                                                subData.put(SelectProductEnum.PRICE.getAttr(), Arith.div(price, businessUnitItem.getRate(), subFieldAttrEntity.getAccuracy()));
                                                subData.put(SelectProductEnum.BUSINESS_PRICE.getAttr(), price);
                                            }
                                        }
                                        break;
                                    case GUARANTEE_PERIOD:
                                        Object obj = subData.get(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr());
                                        if (Objects.nonNull(obj) && !StringUtil.isDigital(obj.toString(),0)){
                                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205077);
                                        }else {
                                            Long guaranteePeriod = subData.getLong(attr);
                                            if (Objects.nonNull(guaranteePeriod) && (guaranteePeriod > 36500L || guaranteePeriod < 0L)) {
                                                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205063, String.format(ProductErrorCodeEnum.API_ERROR_205063.getMsg(), parentAttrName));

                                            }
                                        }
                                        break;
                                    case MEMO:
                                        String memo = subData.getString(attr);
                                        memo = StringUtil.filterEmoji(memo);
                                        if (memo.length() > BasicConstant.PRODUCT_MEMO_MAX_LENGTH) {
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100056,parentAttrName + "." + SystemErrorCodeEnum.API_ERROR_100056.getMsg());
                                        }
                                        break;
                                    case LOSS_RATE:
                                        String lossRateStr = subData.getString(attr);
                                        if (!StringUtil.isDigital(lossRateStr)) {
                                            throw new XbbException(BomErrorCodeEnum.API_ERROR_242023,parentAttrName + "." + BomErrorCodeEnum.API_ERROR_242023.getMsg());
                                        }
                                        if (Double.valueOf(lossRateStr) < 0D || Double.valueOf(lossRateStr) >= 100D) {
                                            throw new XbbException(BomErrorCodeEnum.API_ERROR_242024,parentAttrName + "." + BomErrorCodeEnum.API_ERROR_242024.getMsg());
                                        }
                                        break;
                                    case BATCH:
                                        String batchStr = subData.getString(attr);
                                        if (Objects.equals(subFieldAttrEntity.getFieldType(),FieldTypeEnum.BATCH.getType())){
                                            try {
                                                JSONObject batchObj = JSONObject.parseObject(batchStr);
                                                batchStr = batchObj.getString("value");
                                                subData.put(attr,batchStr);
                                            }catch (Exception e){
                                                //subData.put(attr,"");
                                            }
                                        } else if (StringUtil.isNotEmpty(batchStr)) {
                                            if (batchStr.contains("_")) {
                                                throw new XbbException(InstockErrorCodeEnum.API_ERROR_210035);
                                            }
                                        }
                                        break;
                                    case WAREHOUSE:
                                        //采购退货单不用校验仓库
                                        if (!Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE.getCode())) {
                                            JSONArray warehouseArray;
                                            try {
                                                warehouseArray = subData.getJSONArray(attr);
                                            } catch (Exception e) {
                                                warehouseArray = new JSONArray();
                                            }
                                            if (warehouseArray.isEmpty()) {
                                                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205051, ProductErrorCodeEnum.API_ERROR_205051.getMsg(), parentAttrName);
                                            }
                                        }
                                        break;
                                    default:
                                        break;
                                }
                            }else {
                                validateSubFormData(attr,subValidateData,fieldAttrEntity);
                            }
                        }
                     if (!subData.containsKey(SelectProductEnum.MEMO.getAttr())){
                         subData.put(SelectProductEnum.MEMO.getAttr(),"");
                     }
                    }
                    // 选择产品子表单内的单行文本、多行文本字符限制校验
                    subFormTextCharLimit(subExplainMap, jsonArray);
                } else if (ExplainUtil.isSubForm(fieldAttrEntity.getFieldType())){
                    // 子表单数据校验
                    JSONArray jsonArray = data.getJSONArray(key);
                    List<? extends FieldAttrEntity> subJSONArray = explainMap.get(key).getSubForm().getItems();
                    Map<String, FieldAttrEntity> subExplainMap = ExplainUtil.getExplainMapByList(subJSONArray);
                    Boolean visible = true;
                    if (Objects.equals(fieldAttrEntity.getVisible(), 1)) {
                        visible = !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
                    }
                    if (Objects.equals(fieldAttrEntity.getRequired(), 1) && CollectionsUtil.isEmpty(jsonArray) && visible) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, fieldAttrEntity.getAttrName());
                    }
                    if (Objects.isNull(jsonArray)) {
                        continue;
                    }
                    // 子表单，明日/下周/下月计划等限制行数
                    boolean subFormNum = (Objects.isNull(validateDataDTO.getDataId()) || Objects.equals(validateDataDTO.getDataId(), 0L))  && jsonArray.size() > subFormRowNum;
                    // 联系电话限制10行
                    boolean contactNum = (Objects.isNull(validateDataDTO.getDataId()) || Objects.equals(validateDataDTO.getDataId(), 0L)) && Objects.equals(FieldTypeEnum.CONTACT_NUMBER.getType(), fieldAttrEntity.getFieldType()) && jsonArray.size() > 10;
                    if (contactNum) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.SUB_FORM_MAX_LARGE_MEMO, fieldAttrEntity.getAttrName(), 10);
                    }else if (subFormNum) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.SUB_FORM_MAX_LARGE_MEMO, fieldAttrEntity.getAttrName());
                    }
                    // 校验子表单的必填
                    validateRequire(jsonArray, fieldAttrEntity, subExplainMap, handlerExplainDTO);
                    validateRequire4NextPlan(jsonArray, fieldAttrEntity, handlerExplainDTO);
                    //子表单(电话)内部自身防重复校验（noRepeat自定义为不可重复的时候）
                    if (subExplainMap.containsKey("text_2")) {
                        FieldAttrEntity entity2 = subExplainMap.get("text_2");
                        if (entity2 != null && Objects.equals(entity2.getNoRepeat(), 1) && Objects.nonNull(jsonArray)) {
                            for (int i = 0; i < jsonArray.size(); i++) {
                                JSONObject jsonObject1 = jsonArray.getJSONObject(i);
                                if (Objects.nonNull(jsonObject1)) {
                                    if (Objects.nonNull(jsonObject1.get("text_2"))) {
                                        String value1 = jsonObject1.getString("text_2");
                                        for (int j = i + 1; j < jsonArray.size(); j++) {
                                            JSONObject jsonObject2 = jsonArray.getJSONObject(j);
                                            if (Objects.nonNull(jsonObject2)) {
                                                if (Objects.nonNull(jsonObject2.get("text_2"))) {
                                                    String value2 = jsonObject2.getString("text_2");
                                                    if (Objects.equals(value1, value2)) {
                                                        throw new XbbException(ContactErrorCodeEnum.API_ERROR_204010, ContactErrorCodeEnum.API_ERROR_204010.getMsg(), fieldAttrEntity.getAttrName(), value1);
                                                    }
                                                }
                                            }

                                        }
                                    }
                                }
                            }
                        }
                    }
                    for (Object object: jsonArray) {
                        ValidateDataDTO subValidateData = new ValidateDataDTO();
                        BeanUtil.copyProperties(validateDataDTO, subValidateData);
                        JSONObject subData = (JSONObject) object;
                        subValidateData.setData(subData);
                        subValidateData.setExplainMap(subExplainMap);
                        validateSubFormData(subValidateData, fieldAttrEntity);
                    }
                    // 子表单内的单行文本、多行文本字符限制校验。今日计划其实是明日计划，内容不校验长度
                    if (!(XbbRefTypeEnum.isWorkReportForm(handlerExplainDTO.getBusinessType()) && Objects.equals(WorkReportFieldEnum.THIS_PLAN.getAttr(), fieldAttrEntity.getAttr()))) {
                        subFormTextCharLimit(subExplainMap, jsonArray);
                    }
                } else if (FieldTypeEnum.BIRTHDAY.getType().equals(fieldType)){
                    // 客户生日校验
                    if (data.get(key) instanceof Map) {
                        Map<String, Object> birthdayMap =(Map<String, Object>) data.get(key);
                        String birthday = (String)birthdayMap.get("birthday");
                        // 是否为农历生日
                        boolean isFlag  = false;
                        if (Objects.nonNull(birthdayMap.get("birthdayFlag"))) {
                            isFlag= (int)birthdayMap.get("birthdayFlag") == 1;
                        }
                        // 公历生日时间（10位int的timestamp）
                        Integer solarDay = 0;
                        // 农历时间（字符串）例1994-02-21
                        String lunarDay = "";
                        if (isFlag){
                            lunarDay = birthday;
                        }else {
                            solarDay = DateUtil.getInt(birthday, new SimpleDateFormat("yyyy-MM-dd"));
                        }
                        try {
                            if (CalendarUtil.isBeyondToday(solarDay, lunarDay, isFlag)) {
                                throw new XbbException(BuinessDefaultErrorCodeEnum.API_ERROR_200010, BuinessDefaultErrorCodeEnum.API_ERROR_200010.getMsg());
                            }
                        }  catch (XbbException e) {
                            throw e;
                        }  catch (Exception e) {
                            throw new XbbException(BuinessDefaultErrorCodeEnum.API_ERROR_200011, BuinessDefaultErrorCodeEnum.API_ERROR_200011.getMsg());
                        }
                    }
                } else if(AliasEnum.TEXT.getAlias().equals(esAlias)){
                    // 按照需要添加
                    if (StringUtil.isNotEmpty(value)) {
                        Integer patternType = fieldAttrEntity.getPatternType();

                        if (Objects.equals(patternType, PatternEnum.POSTCODE.getType()) && !RegexUtil.isZipCode(value)) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.POSTCODE.getMsg()));
                        } else if (Objects.equals(patternType, PatternEnum.EMAIL.getType()) && !RegexUtil.isEmail(value.trim())) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.EMAIL.getMsg()));
                        } else if (Objects.equals(patternType, PatternEnum.PHONE.getType()) && !RegexUtil.isPhone(value)) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.PHONE.getMsg()));
                        } else if (Objects.equals(patternType, PatternEnum.MOBILE.getType()) && !RegexUtil.isMobile(value)) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.MOBILE.getMsg()));
                        }
                        // 单行文本和多行文本字符限制检验
                        if (Objects.equals(fieldType, FieldTypeEnum.TEXT.getType()) || Objects.equals(fieldType, FieldTypeEnum.TEXTAREA.getType())) {
                            textCharLimit(fieldAttrEntity, value);
                        }
                        //单行文本
                        if (FieldTypeEnum.TEXT.getType().equals(fieldType) && value.length() > AliasEnum.TEXT.getMax()) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.MORE_THAN_LENGTH, explainMap.get(key).getAttrName(), AliasEnum.TEXT.getMax());
                        } else if(FieldTypeEnum.TEXTAREA.getType().equals(fieldType) && value.length() > AliasEnum.TEXTAREA.getMax()){
                            //多行文本
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.MORE_THAN_LENGTH, explainMap.get(key).getAttrName(),AliasEnum.TEXTAREA.getMax());
                        }
                    }
                }else if("file".equals(esAlias)){
                    //超链接
                    if(value != null && FieldTypeEnum.HREF.getType().equals(fieldType)  && value.length() > AliasEnum.HREF.getMax()){
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.MORE_THAN_LENGTH, explainMap.get(key).getAttrName(),AliasEnum.HREF.getMax());
                    }
                    if (value != null && Objects.equals(fieldType, FieldTypeEnum.UPLOAD.getType())) {
                        try {
                            JSONArray jsonArray = data.getJSONArray(key);
                            List<PaasAttachmentEntityExt> paasAttachmentEntityExts = JSONArray.parseArray(JSON.toJSONString(jsonArray), PaasAttachmentEntityExt.class);
                            validateAttachment(paasAttachmentEntityExts);
                        } catch (Exception e) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FILE_FORMAT_FAIL);
                        }
                    }
                }else if(EsAliasEnum.ARRAY.getEsAlias().equals(esAlias)){
                    //人员多选的校验
                    if(value != null && fieldAttrEntity.getRequired().equals(1) && (FieldTypeEnum.USER_GROUP.getType().equals(fieldType) || FieldTypeEnum.LINK_DATA_MULTI.getType().equals(fieldType))){
                        if(CollectionsUtil.isEmpty(data.getJSONArray(key))){
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, fieldAttrEntity.getAttrName());
                        }
                    }
                    // 关联数据多选加上校验
                    if (Objects.nonNull(value) && FieldTypeEnum.LINK_DATA_MULTI.getType().equals(fieldType)) {
                        JSONArray multiDataArray = data.getJSONArray(key);
                        List idList = multiDataArray.stream().filter(Objects::nonNull).collect(Collectors.toList());
                        data.put(key, idList);
                        if (idList.size() > PaasConstant.LINK_DATA_MULTI_MAX_SIZE) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_OVER_LIMIT, fieldAttrEntity.getAttrName());
                        }
                    }
                }else if(FieldTypeEnum.RICH_TEXT.getType().equals(fieldType)){
                    //富文本校验
                    if (Objects.equals(fieldAttrEntity.getRequired(), 1) && value != null){
                        CharLimitsPoJo charLimits = explainMap.get(RichTextEnum.RICH_TEXT.getAttr()).getCharLimits();
                        Integer min = Optional.ofNullable(charLimits.getMin()).orElse(BasicConstant.ZERO);
                        Integer max = Optional.ofNullable(charLimits.getMax()).orElse(BasicConstant.RICH_TEXT_MAX_CHAR_LIMIT);
                        if (min > value.length() || max < value.length()){
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.RICH_TEXT_LENGTH_ERROR, min, max);
                        }
                    }
                }
                if(Objects.equals(fieldAttrEntity.getNoRepeat(),1)){
                    FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
                    BeanUtil.copyProperties(validateDataDTO, formDataRepeatDTO);
                    formDataRepeatDTO.setValue(data.get(key));
                    formDataRepeatDTO.setAttr(key);
                    // 移除serialNo 以免查重
                    if (!Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.SERIALNO.getType())) {
                        formDataRepeatDTO.setSerialNo(null);
                    }

                    repeat(formDataRepeatDTO, validateDataDTO, fieldAttrEntity);
                }

            }//
            // 校验编号
            if (StringUtil.isNotEmpty(validateDataDTO.getSerialNo())) {
                String serialNo = validateDataDTO.getSerialNo();
                FieldAttrEntity fieldAttrEntity = explainMap.get(FieldTypeEnum.SERIALNO.getAlias());
                if (!Objects.isNull(fieldAttrEntity) && Objects.equals(1, fieldAttrEntity.getNoRepeat())) {
                    FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
                    BeanUtil.copyProperties(validateDataDTO, formDataRepeatDTO);
                    formDataRepeatDTO.setSerialNo(serialNo);
                    formDataRepeatDTO.setAttr(FieldTypeEnum.SERIALNO.getAlias());
                    formDataRepeatDTO.setValue(serialNo);
//
                    repeat(formDataRepeatDTO, validateDataDTO, fieldAttrEntity);
                }
            }
        }catch (XbbException e) {
            throw e;
        }catch (Exception e) {
            LOG.error("formDataValidateDataHelp.validateData  校验数据的正常格式", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    private void subFormTextCharLimit(Map<String, FieldAttrEntity> subExplainMap, JSONArray jsonArray) throws XbbException {
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject subData = jsonArray.getJSONObject(i);
            for (String keyAttr : subData.keySet()) {
                FieldAttrEntity subFieldAttrEntity = subExplainMap.get(keyAttr);
                if (Objects.isNull(subFieldAttrEntity)) {
                    continue;
                }
                Integer subFieldType = subFieldAttrEntity.getFieldType();
                if (Objects.equals(subFieldType, FieldTypeEnum.TEXT.getType()) || Objects.equals(subFieldType, FieldTypeEnum.TEXTAREA.getType())) {
                    String subValue = subData.getString(keyAttr);
                    // 单行文本和多行文本字符限制检验
                    textCharLimit(subFieldAttrEntity, subValue);
                }
            }
        }
    }

    private void textCharLimit(FieldAttrEntity fieldAttrEntity, String value) throws XbbException {
        Integer fieldType = fieldAttrEntity.getFieldType();
        if (StringUtil.isNotEmpty(value) && (FieldTypeEnum.TEXT.getType().equals(fieldType) || FieldTypeEnum.TEXTAREA.getType().equals(fieldType))) {
            CharLimitsPoJo charLimits = fieldAttrEntity.getCharLimits();
            if (Objects.isNull(charLimits)) {
                // 用户未主动进行字符限制设置时取默认限制
                charLimits = new CharLimitsPoJo(BasicConstant.ZERO, FieldTypeEnum.TEXT.getType().equals(fieldType) ? AliasEnum.TEXT.getMax() : AliasEnum.TEXTAREA.getMax());
            }
            Integer min = Optional.ofNullable(charLimits.getMin()).orElse(BasicConstant.ZERO);
            Integer max = Optional.ofNullable(charLimits.getMax()).orElse(FieldTypeEnum.TEXT.getType().equals(fieldType) ? AliasEnum.TEXT.getMax() : AliasEnum.TEXTAREA.getMax());
            if (min > value.length() || max < value.length()) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.TEXT_LENGTH_ERROR, fieldAttrEntity.getAttrName(), min, max);
            }
        }
    }

    public void validateOuterLinkData(JSONObject data, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        for (String key : data.keySet()) {
            FieldAttrEntity fieldAttrEntity = explainMap.get(key);
            Integer fieldType = fieldAttrEntity.getFieldType();
            if (Objects.equals(fieldType, FieldTypeEnum.SELECT_PRODUCT.getType())) {
                JSONArray jsonArray = data.getJSONArray(key);
                if (CollectionsUtil.isEmpty(jsonArray)) {
                    continue;
                }
                List<? extends FieldAttrEntity> subJSONArray = explainMap.get(key).getSubForm().getItems();
                Map<String, FieldAttrEntity> subExplainMap = ExplainUtil.getExplainMapByList(subJSONArray);
                // 选择产品子表单内的单行文本、多行文本字符限制校验
                subFormTextCharLimit(subExplainMap, jsonArray);
            } else if (ExplainUtil.isSubForm(fieldType)) {
                JSONArray jsonArray = data.getJSONArray(key);
                if (CollectionsUtil.isEmpty(jsonArray)) {
                    continue;
                }
                List<? extends FieldAttrEntity> subJSONArray = explainMap.get(key).getSubForm().getItems();
                Map<String, FieldAttrEntity> subExplainMap = ExplainUtil.getExplainMapByList(subJSONArray);
                // 子表单内的单行文本、多行文本字符限制校验
                subFormTextCharLimit(subExplainMap, jsonArray);
            } else if (Objects.equals(fieldType, FieldTypeEnum.TEXT.getType()) || Objects.equals(fieldType, FieldTypeEnum.TEXTAREA.getType())) {
                String value = data.getString(key);
                if (StringUtil.isEmpty(value)) {
                    continue;
                }
                // 单行文本和多行文本字符限制检验
                textCharLimit(fieldAttrEntity, value);
            }
        }
    }

    /**
     * 子表单校验必填
     * @param jsonArray
     * @param fieldAttrEntity
     * @param subExplainMap
     * @param handlerExplainDTO
     * @throws XbbException
     */
    public static void validateRequire(JSONArray jsonArray, FieldAttrEntity fieldAttrEntity, Map<String, FieldAttrEntity> subExplainMap, HandlerExplainDTO handlerExplainDTO) throws XbbException {
        for (int i =0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            // 兼容没有该行记录的情况 如[{}]
            if (Objects.equals(jsonObject.size(),0)){
                continue;
            }
            Iterator<String> subIterator = subExplainMap.keySet().iterator();
            while (subIterator.hasNext()) {
                String subKey = subIterator.next();
                FieldAttrEntity subField = subExplainMap.get(subKey);
                if (Objects.equals(subField.getVisible(), 0) || Objects.equals(subField.getIsOpen(), 2)){
                    continue;
                }
                Boolean visible = !ExplainUtil.invisibleOfScope(subField, handlerExplainDTO);
                // 子表单校验
                if (visible && Objects.equals(subField.getRequired(), 1) && StringUtil.isEmpty(jsonObject.getString(subKey))) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED,
                            fieldAttrEntity.getAttrName() + subField.getAttrName());
                }
            }

        }
    }
    /**
     * 工作报告－明日计划子表单校验必填
     * @param jsonArray
     * @param fieldAttrEntity
     * @param handlerExplainDTO
     * @throws XbbException
     */
    public static void validateRequire4NextPlan(JSONArray jsonArray, FieldAttrEntity fieldAttrEntity, HandlerExplainDTO handlerExplainDTO) throws XbbException {
        if (!XbbRefTypeEnum.isWorkReportForm(handlerExplainDTO.getBusinessType())) {
            return;
        }
        if (!Objects.equals(WorkReportFieldEnum.NEXT_PLAN.getAttr(), fieldAttrEntity.getAttr())) {
            return;
        }
        // 30389 【成功部】日报/周报，明日计划/下周计划，虽然设置了必填项，但是如果点开了不写任何文字，也可以保存。没有达到必填的效果
        if (jsonArray.size() > AttrLenthConstant.PLAN_NUM_MAX_LENGTH) {
            throw new XbbException(WorkReportErrorCodeEnum.API_ERROR_207009);
        }
        boolean nextPlanContentEmpty = true;
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject nextPlan = jsonArray.getJSONObject(i);
            String nextPlanContent = nextPlan.getString(WorkReportPlanEnum.CONTENT.getAttr());
            if (!StringUtil.isEmpty(nextPlanContent) && nextPlanContent.length() > AttrLenthConstant.PLAN_CONTENT_MAX_LENGTH) {
                throw new XbbException(WorkReportErrorCodeEnum.API_ERROR_207003);
            }
            if (!StringUtil.isEmpty(nextPlanContent)) {
                nextPlanContentEmpty = false;
            }
        }
        boolean visible = !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);

        if (visible && Objects.equals(BasicConstant.ONE, fieldAttrEntity.getRequired()) && nextPlanContentEmpty) {
            throw new XbbException(WorkReportErrorCodeEnum.API_ERROR_207023, WorkReportErrorCodeEnum.API_ERROR_207023.getMsg(), I18nMessageUtil.getMessage(fieldAttrEntity.getAttrName()));
        }
    }

    public Map<String, Boolean> getValidateFieldMap(ValidateDataDTO validateDataDTO, JSONObject data, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        // 关联选项
        List<Integer> fieldList = Arrays.asList(FieldTypeEnum.COMBO.getType(), FieldTypeEnum.RADIO_GROUP.getType(), FieldTypeEnum.CHECKBOX_GROUP.getType(), FieldTypeEnum.COMBO_CHECK.getType(), FieldTypeEnum.STAGE_THRUSTER.getType());
        // true 需要校验， false 不需要校验
        Map<String, Boolean> validateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Map.Entry<String, FieldAttrEntity> entry : explainMap.entrySet()) {
            String attr = entry.getKey();
            FieldAttrEntity item = entry.getValue();
            if (fieldList.contains(item.getFieldType())) {
                if(Objects.equals(BasicConstant.TWO, item.getIsOpen())){
                    // 回收站的不处理
                    continue;
                }
                List<ItemPoJo> itemPoJos = new ArrayList<>();
                if (Objects.equals(item.getFieldType(), FieldTypeEnum.STAGE_THRUSTER.getType())) {
                    itemPoJos = proFormHelp.getEditStageComboItems(item, validateDataDTO.getSaasMark(), validateDataDTO.getBusinessType(), validateDataDTO.getCorpid(), validateDataDTO.getFormId(), data);
                } else {
                    itemPoJos = proFormHelp.getItem(item, validateDataDTO.getBusinessType(), validateDataDTO.getCorpid(), validateDataDTO.getFormId());
                }
                Map<String, List<String>> itemMap = new HashMap<>(itemPoJos.size());
                itemPoJos.forEach(itemPoJo -> {
                    List<String> fieldMap = itemPoJo.getFieldMap();
                    if (fieldMap != null && !fieldMap.isEmpty()) {
                        itemMap.put(itemPoJo.getValue().toString(), fieldMap);
                        fieldMap.forEach(field -> validateMap.put(field, false));
                    }
                });
                Object object = data.get(attr);
                if (object != null) {
                    if (object instanceof List) {
                        List list = (List) object;
                        list.forEach(value -> {
                            if (itemMap.containsKey(value.toString())) {
                                itemMap.get(value.toString()).forEach(attrField -> validateMap.put(attrField, true));
                            }
                        });
                    } else {
                        if (itemMap.containsKey(object.toString())) {
                            itemMap.get(object.toString()).forEach(attrField -> validateMap.put(attrField, true));
                        }
                    }
                }
            }
        }
        // 跟进记录新建编辑时，如果不选下次提醒，那么以下几个字段的必填就不校验了
        boolean communicateSpecial = Objects.equals(validateDataDTO.getBusinessType(), XbbRefTypeEnum.CUSTOMER_COMMUNICATE.getCode()) && !Objects.equals(data.get(CustomerCommunicateEnum.IF_REMIND.getAttr()), 1) && !Objects.equals(validateDataDTO.getIsImport(), 1);
        if (communicateSpecial) {
            validateMap.put(CustomerCommunicateEnum.NEXT_COMMUNICATE_TIME.getAttr(), false);
            validateMap.put(CustomerCommunicateEnum.NOTIFY_TYPE.getAttr(), false);
            validateMap.put(CustomerCommunicateEnum.NOTIFY_TIME.getAttr(), false);
            validateMap.put(CustomerCommunicateEnum.NEXT_NOTIFY_MEMO.getAttr(), false);
        }
        boolean productSpecial = Objects.equals(validateDataDTO.getBusinessType(), XbbRefTypeEnum.PRODUCT.getCode()) && !Objects.equals(data.get(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr()), 1) && !Objects.equals(validateDataDTO.getIsImport(), 1);
        if (productSpecial) {
            validateMap.put(ProductEnum.SHELF_LIFE_DAYS.getAttr(), false);
            validateMap.put(ProductEnum.EXPIRATION_ALERT_DAYS.getAttr(), false);
        }
        return validateMap;
    }

    private void validateAttachment(List<PaasAttachmentEntityExt> paasAttachmentEntityExts) throws XbbException {
        for (PaasAttachmentEntityExt entityExt : paasAttachmentEntityExts) {
            if (Objects.isNull(entityExt.getAttachIndex()) || Objects.isNull(entityExt.getExt()) || Objects.isNull(entityExt.getFilename()) || Objects.isNull(entityExt.getSize())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100026);
            }
        }
    }

    /**
     * 校验不可重复数据
     * @param formDataRepeatDTO 校验数据重复的对象
     * @param validateDataDTO 前端传参
     * @param fieldAttrEntity 字段属性
     * @throws XbbException
     */
    public void repeat(FormDataRepeatDTO formDataRepeatDTO, ValidateDataDTO validateDataDTO, FieldAttrEntity fieldAttrEntity) throws XbbException {
        try {
            CheckRepeatVO checkRepeatVO = repeatHelp.repeat(formDataRepeatDTO);
            if (Objects.nonNull(checkRepeatVO.getManyRepeat()) && checkRepeatVO.getManyRepeat()) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100083, SystemErrorCodeEnum.API_ERROR_100083.getMsg(), fieldAttrEntity.getAttrName());
            }
            if (Objects.nonNull(checkRepeatVO.getNoRepeat()) && !checkRepeatVO.getNoRepeat()) {
                if (Objects.nonNull(checkRepeatVO.getApprovalRepeat()) && checkRepeatVO.getApprovalRepeat()) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, ErrorMessageConstant.DUPLICATE_DATA_APPROVAL, fieldAttrEntity.getAttrName());
                }
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100034, MessageConstant.DUPLICATE_VALUE, fieldAttrEntity.getAttrName());
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("formDataValidateDataHelp.repeat 出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 子表单校验
     * @param validateDataDTO 参数实体
     * @param parentFieldAttrEntity 子表单的字段实体
     * @throws XbbException
     */
    public void validateSubFormData(ValidateDataDTO validateDataDTO, FieldAttrEntity parentFieldAttrEntity) throws XbbException{
        try {
            JSONObject data = validateDataDTO.getData();
            Integer isImport = validateDataDTO.getIsImport();
            Map<String, FieldAttrEntity> explainMap = validateDataDTO.getExplainMap();
            String platform = validateDataDTO.getPlatform();
            Iterator iterator = data.keySet().iterator();
            String numEsAlias = SubFieldTypeEnum.NUM.getEsalias();
            String locationEsAlias = SubFieldTypeEnum.LOCATION.getEsalias();
            String addressEsAlias = SubFieldTypeEnum.ADDRESS.getEsalias();
            Integer businessType = validateDataDTO.getBusinessType();
            //如果是线索、客户、联系人业务则不走之前的逻辑，走新的逻辑
            boolean recheckFlag = Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode()) || Objects.equals(businessType, XbbRefTypeEnum.CLUE.getCode()) ||
                    Objects.equals(businessType, XbbRefTypeEnum.CONTACT.getCode());
            if (recheckFlag){
                while (iterator.hasNext()){
                    String key = iterator.next().toString();
                    // 子表单id不检查
                    if (Objects.equals(key, BusinessConstant.SUB_ID) || Objects.equals(key, BusinessConstant.PRODUCT_SUB_ID) || Objects.equals(key, BusinessConstant.REF_PRODUCT_ID)) {
                        continue;
                    }
                    FieldAttrEntity fieldAttrEntity = explainMap.get(key);
                    if (Objects.isNull(fieldAttrEntity)) {
                        continue;
                    }
                    if(Objects.equals(fieldAttrEntity.getNoRepeat(),1)){
                        FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
                        BeanUtil.copyProperties(validateDataDTO, formDataRepeatDTO);
                        formDataRepeatDTO.setValue(data.get(key));
                        formDataRepeatDTO.setAttr(parentFieldAttrEntity.getAttr());
                        formDataRepeatDTO.setSubAttr(key);
                        // 以免查重serialNo
                        if (!Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.SERIALNO.getType())) {
                            formDataRepeatDTO.setSerialNo(null);
                        }
//                        formDataRepeatDTO.setIsEdit(true);
                        repeat(formDataRepeatDTO, validateDataDTO, fieldAttrEntity);
                    }
                    if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.NUM.getType())) {
                        String num = data.getString(key);
                        if(num == null) {
                            break;
                        }
                        validateNum(key, explainMap, data, businessType,isImport);
                    }
                }

            }else {
                while (iterator.hasNext()){
                    String key = iterator.next().toString();
                    // 子表单id不检查
                    if (Objects.equals(key, BusinessConstant.SUB_ID) || Objects.equals(key, BusinessConstant.PRODUCT_SUB_ID) || Objects.equals(key, BusinessConstant.REF_PRODUCT_ID)) {
                        continue;
                    }
                    String[] array = key.split("_");
                    FieldAttrEntity fieldAttrEntity = explainMap.get(key);
                    if (Objects.isNull(fieldAttrEntity)) {
                        continue;
                    }
                    if(Objects.equals(fieldAttrEntity.getNoRepeat(),1)){
                        FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
                        BeanUtil.copyProperties(validateDataDTO, formDataRepeatDTO);
                        formDataRepeatDTO.setValue(data.get(key));
                        formDataRepeatDTO.setAttr(parentFieldAttrEntity.getAttr());
                        formDataRepeatDTO.setSubAttr(key);
                        // 以免查重serialNo
                        if (!Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.SERIALNO.getType())) {
                            formDataRepeatDTO.setSerialNo(null);
                        }
                        // 添加联系人电话有重复时（全局），报错内容增加具体号码，下方过滤条件text_2为过滤到子表单内电话
                        if (Objects.equals(formDataRepeatDTO.getBusinessType(), XbbRefTypeEnum.CONTACT.getCode())
                                && Objects.equals(fieldAttrEntity.getParentAttr(), ContactEnum.PHONE.getAttr())
                                && Objects.equals(fieldAttrEntity.getAttr(), "text_2")) {
                            // 判断是否重复
                            CheckRepeatVO checkRepeatVO = repeatHelp.repeat(formDataRepeatDTO);
                            if (Objects.equals(checkRepeatVO.getManyRepeat(), true) || Objects.equals(checkRepeatVO.getNoRepeat(), false)) {
                                throw new XbbException(ContactErrorCodeEnum.API_ERROR_204009, ContactErrorCodeEnum.API_ERROR_204009.getMsg(), formDataRepeatDTO.getValue());
                            }
                        }
                        /**
                         * 当搜客转客户时，该客户无客户电话时，用“********”补全客户电话，且避免判重逻辑（产品定的方案）
                         */
                        if (Objects.equals(fieldAttrEntity.getAttr(), CustomerManagementEnum.PHONE.getAttr())) {
                            LOG.info("校验数据---" + validateDataDTO.toString());
                        }
                        boolean isSouke = Objects.equals(validateDataDTO.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode()) &&
                                Objects.equals(fieldAttrEntity.getParentAttr(), CustomerManagementEnum.PHONE.getAttr())
                                && Objects.equals(formDataRepeatDTO.getValue(), StringConstant.SOUKE_TELE_VALUE);
                        if (!isSouke) {
                            formDataRepeatDTO.setSubDataId(data.getLong("subDataId"));
                            repeat(formDataRepeatDTO, validateDataDTO, fieldAttrEntity);
                        }
                    }
                    if(array[0].equals(numEsAlias)){
                        String num = data.getString(key);
                        if(num == null) {
                            break;
                        }
                        validateNum(key, explainMap, data, businessType,isImport);
                    }else if(array[0].equals(locationEsAlias)) {
                        if(platform.equals(PlatFormEnum.WEB.getValue())) {
                            break;
                        }
                        validateLocation(key, explainMap, data);
                    }else if (Objects.equals(FieldTypeEnum.DATETIME.getType(), fieldAttrEntity.getFieldType()) && Objects.equals(fieldAttrEntity.getIsRedundant(), 1)) {
                        // 自定义字段日期校验
                        validateDateTime(key, explainMap, data);
                    } else if (array[0].equals(addressEsAlias)){
                        validateAddress(key, explainMap, data);
                    } else if(EsAliasEnum.ARRAY.getEsAlias().equals(array[0])){

                    } else {
                        Integer patternType = fieldAttrEntity.getPatternType();
                        if (StringUtil.isNotEmpty(data.getString(key))) {
                            if (Objects.equals(patternType, PatternEnum.POSTCODE.getType()) && !RegexUtil.isZipCode(data.getString(key))) {
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.POSTCODE.getMsg()));
                            } else if (Objects.equals(patternType, PatternEnum.EMAIL.getType()) && !RegexUtil.isEmail(data.getString(key))) {
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.EMAIL.getMsg()));
                            } else if (Objects.equals(patternType, PatternEnum.PHONE.getType()) && !RegexUtil.isPhone(data.getString(key))) {
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.PHONE.getMsg()));
                            } else if (Objects.equals(patternType, PatternEnum.MOBILE.getType()) && !RegexUtil.isMobile(data.getString(key))) {
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.MOBILE.getMsg()));
                            }
                        }

                    }
                }
            }
        } catch (XbbException e) {
            throw e;
        }catch (Exception e) {
            LOG.error("校验数据的正常格式", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 校验日期格式
     * @param key 字段key值
     * @param explainMap 字段容器
     * @param data 数据
     * @throws XbbException
     */
    private void validateDateTime(String key, Map<String, FieldAttrEntity> explainMap, JSONObject data) throws XbbException {
        // 自定义字段日期校验
        if ( Objects.isNull(data.get(key))) {
            return;
        }
        try {
            Long.parseLong(data.getString(key));
        } catch (NumberFormatException e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, MessageConstant.DATA_NOT_STANDARD, explainMap.get(key).getAttrName());
        }
    }
    /**
     * 校验数字格式
     * @param key 字段key值
     * @param validateDataDTO 字段容器
     * @param parentFieldAttrEntity 数据
     * @throws XbbException
     */
    public void validateSubFormData(String key,ValidateDataDTO validateDataDTO,FieldAttrEntity parentFieldAttrEntity) throws XbbException{
        JSONObject data = validateDataDTO.getData();
        Integer isImport = validateDataDTO.getIsImport();
        Map<String, FieldAttrEntity> explainMap = validateDataDTO.getExplainMap();
        String platform = validateDataDTO.getPlatform();
        Iterator iterator = data.keySet().iterator();
        String numEsAlias = SubFieldTypeEnum.NUM.getEsalias();
        String locationEsAlias = SubFieldTypeEnum.LOCATION.getEsalias();
        String addressEsAlias = SubFieldTypeEnum.ADDRESS.getEsalias();
        Integer businessType = validateDataDTO.getBusinessType();
        // 子表单id不检查
        if (Objects.equals(key, BusinessConstant.SUB_ID)) {
            return;
        }
        String[] array = key.split("_");
        FieldAttrEntity fieldAttrEntity = explainMap.get(key);
        // 字段回收站删除的不校验
        if (Objects.equals(fieldAttrEntity.getIsOpen(), 2)) {
            return;
        }

        if(Objects.equals(fieldAttrEntity.getNoRepeat(),1)){
            FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
            BeanUtil.copyProperties(validateDataDTO, formDataRepeatDTO);
            formDataRepeatDTO.setValue(data.get(key));
            formDataRepeatDTO.setAttr(parentFieldAttrEntity.getAttr());
            formDataRepeatDTO.setSubAttr(key);
            // 以免查重serialNo
            if (!Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.SERIALNO.getType())) {
                formDataRepeatDTO.setSerialNo(null);
            }
            repeat(formDataRepeatDTO, validateDataDTO, fieldAttrEntity);
        }
        if(array[0].equals(numEsAlias)){
            String num = data.getString(key);
            if(num == null) {
                return;
            }
            validateNum(key, explainMap, data, businessType, isImport);
        }else if(array[0].equals(locationEsAlias)) {
            if(platform.equals(PlatFormEnum.WEB.getValue())) {
                return;
            }
            validateLocation(key, explainMap, data);
        } else if (array[0].equals(addressEsAlias)){
            validateAddress(key, explainMap, data);
        } else {
            Integer patternType = fieldAttrEntity.getPatternType();
            if (StringUtil.isNotEmpty(data.getString(key))) {
                if (Objects.equals(patternType, PatternEnum.POSTCODE.getType()) && !RegexUtil.isZipCode(data.getString(key))) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.POSTCODE.getMsg()));
                } else if (Objects.equals(patternType, PatternEnum.EMAIL.getType()) && !RegexUtil.isEmail(data.getString(key))) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.EMAIL.getMsg()));
                } else if (Objects.equals(patternType, PatternEnum.PHONE.getType()) && !RegexUtil.isPhone(data.getString(key))) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.PHONE.getMsg()));
                } else if (Objects.equals(patternType, PatternEnum.MOBILE.getType()) && !RegexUtil.isMobile(data.getString(key))) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, String.format(StringConstant.ERROR_PARAM, fieldAttrEntity.getAttrName(), PatternEnum.MOBILE.getMsg()));
                }
            }
        }
    }

    private void validateNum(String key, Map<String, FieldAttrEntity> explainMap, JSONObject data, Integer businessType, Integer isImport) throws XbbException{
        try {
            String numStr = data.getString(key);
            if (StringUtil.isEmpty(numStr)) {
                data.put(key, null);
                return;
            }
            Double num = null;
            if(Objects.nonNull(numStr) && !"null".equals(numStr)) {
                try {
                    BigDecimal bigDecimal = new BigDecimal(numStr);
                    num = bigDecimal.doubleValue();
                } catch (NumberFormatException e) {
                    LOG.error("validateNum error:" + numStr + ",key:" + key + ",explainMap:" + explainMap.get(key).getAttrName());
                    LOG.error("validateNum error",e);
                    num = null;
                }
            }
            // 校验数据是否是数字
            if(num == null && !(Objects.equals(BasicConstant.ONE, isImport) && Objects.equals(XbbRefTypeEnum.PRODUCT.getCode(), businessType) && Objects.equals(key, ProductEnum.PRICE.getAttr()))){
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, MessageConstant.DATA_NOT_STANDARD, explainMap.get(key).getAttrName());
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("formDataValidateData.validateNum校验数据的正常格式", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 校验定位
     * @param key 字段key值
     * @param explainMap 字段容器
     * @param data 数据
     * @throws XbbException
     */
    public void validateLocation(String key, Map<String, FieldAttrEntity> explainMap, JSONObject data) throws XbbException{
        try {
            FieldAttrEntity fieldAttrEntity = explainMap.get(key);
            if (fieldAttrEntity == null) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.THIS_EXPLAIN_NOT_EXIST);
            }
            String name = fieldAttrEntity.getAttrName();
            JSONObject address = data.getJSONObject(key) == null ? new JSONObject() : data.getJSONObject(key);
            //经纬度超出范围时：经度使用最大值180度,纬度使用最大90度或最小值-90度
            JSONObject location = address.getJSONObject("location");
            Double longitude = location == null ? null : location.getDouble("lon");
            Double latitude = location == null ? null : location.getDouble("lat");
            boolean result = Objects.equals(1, explainMap.get(key).getRequired()) && (longitude == null || latitude == null);
            if(result){
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, name);
            }
            if (Objects.isNull(data.getJSONObject(key))) {
                return;
            }
            // 强制校验定位的key的个数，不符合，抛异常
            if(address.size() != PaasConstant.LOCATION_KEN_LENGTH) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, MessageConstant.DATA_NOT_STANDARD, name);
            }
            if(longitude == null){
                longitude = 0D;
            }
            if(latitude == null){
                latitude = 0D;
            }
            if(longitude >= PaasConstant.LONGITUDE_MAX || longitude <= PaasConstant.LONGITUDE_MIN){
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.LOCATION_FAIL, name);
            }
            if(latitude >= PaasConstant.LATITUDE_MAX || latitude <= PaasConstant.LATITUDE_MIN) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.LOCATION_FAIL, name);
            }
        }catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("formDataValidateData.validateLocation校验数据的正常格式", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 校验地址的数据格式，如果有经纬度，则需要校验经纬度的格式是否正确
     * @author 吴峰
     * @date 2019/3/29 11:28
     * @param key 索引
     * @param explainMap 字段容器
     * @param data 数据
     * @return void
     * @throws XbbException 业务异常
     */
    public void validateAddress(String key, Map<String, FieldAttrEntity> explainMap, JSONObject data) throws XbbException{
        try {
            if(Objects.equals(1, explainMap.get(key).getRequired()) && StringUtil.isEmpty(data.getString(key))){
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, explainMap.get(key).getAttrName());
            }
            JSONObject address = data.getJSONObject(key);
            //具体地址的长度判断
            String specificAddress = "";
            if (Objects.nonNull(address)) {
                if (!address.containsKey(AddressEnum.PROVINCE.getAlias())) {
                    address.put(AddressEnum.PROVINCE.getAlias(), "");
                }
                if (!address.containsKey(AddressEnum.CITY.getAlias())) {
                    address.put(AddressEnum.CITY.getAlias(), "");
                }
                if (!address.containsKey(AddressEnum.DISTRICT.getAlias())) {
                    address.put(AddressEnum.DISTRICT.getAlias(), "");
                }
                if (!address.containsKey(AddressEnum.ADDRESS.getAlias())) {
                    address.put(AddressEnum.ADDRESS.getAlias(), "");
                }
                specificAddress = address.getString("address");
            } else {
                // 如果为null，给默认值，避免保存的时候es插入失败
                JSONObject addressObj = new JSONObject();
                addressObj.put(AddressEnum.PROVINCE.getAlias(), "");
                addressObj.put(AddressEnum.CITY.getAlias(), "");
                addressObj.put(AddressEnum.DISTRICT.getAlias(), "");
                addressObj.put(AddressEnum.ADDRESS.getAlias(), "");
                data.put(key, addressObj);
                return;
            }
            if(StringUtil.isNotEmpty(specificAddress)){
                if(specificAddress.length() > AliasEnum.ADDRESS.getMax()){
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.MORE_THAN_LENGTH, explainMap.get(key).getAttrName(),AliasEnum.ADDRESS.getMax());
                }
            }
            // 强制校验定位的key的个数，不符合，抛异常
            if(address.size() != PaasConstant.LOCATION_KEN_LENGTH && address.size() != PaasConstant.ADDRESS_KEN_LENGTH) {
//                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, String.format(MessageConstant.DATA_NOT_STANDARD, explainMap.get(key).getAttrName()));
            }
            JSONObject location = address.getJSONObject("location");
            if (Objects.nonNull(location) && !location.isEmpty()) {
                validateLocation(key, explainMap, data);
            }
        }catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("formDataValidateData.validateAddress校验数据的正常格式", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    private Map<String, Map<Object, List<String>>> getItemLinkField(Map<String, FieldAttrEntity> explainMap) {
        Iterator<String> iterator = explainMap.keySet().iterator();
        Map<String, Map<Object, List<String>>> itemLinkField = new HashMap<>(explainMap.size());
        while (iterator.hasNext()) {
            FieldAttrEntity fieldAttrEntity = explainMap.get(iterator.next());
            if (ExplainUtil.isCombo(fieldAttrEntity.getFieldType()) && Objects.equals(1, fieldAttrEntity.getComboType())) {
                List<ItemPoJo> itemList = fieldAttrEntity.getItems();
                if (Objects.nonNull(itemList) && !itemList.isEmpty()) {
                    Map<Object, List<String>> valueFiledMap = new HashMap<>(itemList.size());
                    itemList.stream().filter(itemPoJo -> !itemPoJo.getFieldMap().isEmpty()).forEach(itemPoJo -> {
                        List<String> fieldList = itemPoJo.getFieldMap();
                        valueFiledMap.put(itemPoJo.getValue(), fieldList);
                    });
                    itemLinkField.put(fieldAttrEntity.getAttr(), valueFiledMap);
                }
            }
        }
        return itemLinkField;
    }
    /**
     * 是否新建展示且启用加可见
     *
     * @param fieldAttrEntity
     * @author long.rao
     * @date 2019-07-17 14:31
     */
    private boolean showOpenVisiable(FieldAttrEntity fieldAttrEntity) {
        boolean showOpenVisiable = false;
        if (Objects.equals(fieldAttrEntity.getVisible(), 1) && Objects.equals(fieldAttrEntity.getIsOpen(), 1)) {
            //系统字段才有showType
            if(Objects.equals(fieldAttrEntity.getIsRedundant(),0)  ){
                // 这三个条件加起来，新建的时候的字段才会展示出来
                if(ShowTypeEnum.getAddShowType().contains(fieldAttrEntity.getShowType()) ){
                    showOpenVisiable = true;
                }
            }else{
                showOpenVisiable = true;
            }
        }
        return showOpenVisiable;
    }

    /**
     * 客户名称和电话是否判重要去表单配置表查询
     * @param validateDataDTO
     * @param explainMap
     * @author xingxing.xiao
     * @date 2019/11/14 16:09
     */
    public void getCustomerRepeatConfig(ValidateDataDTO validateDataDTO, Map<String, FieldAttrEntity> explainMap, Integer businessType) {
        String nameRepeat = FormConfigEnum.CUSTOMER_NAME_REPEAT_TYPE.getConfig();
        String phoneRepeat = FormConfigEnum.CUSTOMER_PHONE_REPEAT_TYPE.getConfig();
        String nameAttr = CustomerManagementEnum.NAME.getAttr();
        String phoneAttr = CustomerManagementEnum.PHONE.getAttr();
        if (Objects.equals(businessType, XbbRefTypeEnum.CLUE.getCode())) {
            nameRepeat = FormConfigEnum.CLUE_NAME_REPEAT_TYPE.getConfig();
            phoneRepeat = FormConfigEnum.CLUE_PHONE_REPEAT_TYPE.getConfig();
            nameAttr = ClueEnum.COMPANY_NAME.getAttr();
            phoneAttr = ClueEnum.PHONE.getAttr();
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", validateDataDTO.getCorpid());
        param.put("formId", validateDataDTO.getFormId());
        param.put("configIn", Arrays.asList(nameRepeat, phoneRepeat));
        param.put("del", DelEnum.NORMAL.getDel());
        List<FormConfigEntity> formConfigEntityList = formConfigModel.findEntitys(param);
        Map<String, String> formConfigMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (FormConfigEntity configEntity : formConfigEntityList) {
            formConfigMap.put(configEntity.getConfig(), configEntity.getConfigValue());
        }
        FieldAttrEntity nameFieldEntity = explainMap.get(nameAttr);
        if (nameFieldEntity != null && Objects.nonNull(formConfigMap.get(nameRepeat))) {
            // 根据表单配置更改 noRepeat 的值(不启用时noRepeat为0，模板内和全局时noRepeat为1)
            if (Objects.equals(formConfigMap.get(nameRepeat), RepeatTypeEnum.REPEAT_CANCEL.getType().toString())) {
                nameFieldEntity.setNoRepeat(RepeatTypeEnum.REPEAT_CANCEL.getType());
            } else {
                nameFieldEntity.setNoRepeat(RepeatTypeEnum.REPEAT_SINGLE.getType());
            }
        }

        FieldAttrEntity phoneFieldEntity = explainMap.get(phoneAttr);
        if (phoneFieldEntity != null && Objects.nonNull(formConfigMap.get(phoneRepeat))) {
            List<? extends FieldAttrEntity> items = phoneFieldEntity.getSubForm().getItems();
            for (FieldAttrEntity attrEntity : items) {
                if (Objects.equals(attrEntity.getAttr(), "text_2")) {
                    // 根据表单配置更改 noRepeat 的值(不启用时noRepeat为0，模板内和全局时noRepeat为1)
                    if (Objects.equals(formConfigMap.get(phoneRepeat), RepeatTypeEnum.REPEAT_CANCEL.getType().toString())) {
                        attrEntity.setNoRepeat(RepeatTypeEnum.REPEAT_CANCEL.getType());
                    } else {
                        attrEntity.setNoRepeat(RepeatTypeEnum.REPEAT_SINGLE.getType());
                    }
                }
            }
        }
    }
}
